﻿// Copyright 2006 - Morten Nielsen (www.iter.dk)
//
// This file is part of SharpMap.
// SharpMap is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// SharpMap is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public License
// along with SharpMap; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 

using System;
using System.Collections.Generic;
using System.Text;

using Petra.Gis.Geometry.Support;
using Petra.Gis.ReferenceSystemServices;

namespace Petra.Gis.ReferenceSystemServices.Transformations
{


  /// <summary>
  /// Maintains chain of transformations when transformation
  /// between coordinate systems can't be simply done using single transformation.
  /// eg. sourceCS = geographics, targetCS = projection and
  /// projection uses mm as storage units instead of meters.
  /// In this case ProjectionTransform is applied and also ProjectionUnitsTransform follows.
  /// Together both transforms creates chain = ChainedTransform. Chained transform is inversible
  /// only when all used transforms in chain are iversible.
  /// </summary>
  public class ChainedTransform : SRSTransform
  {


    /// <summary>
    /// xaml constr.
    /// </summary>
    public ChainedTransform()
      : base() { }





    //public ChainedTransform(CoordinateSystem sourceCS, CoordinateSystem targetCS)
    //  : this(sourceCS, targetCS, new TransformList(), false)
    //{
    //}





    public ChainedTransform(TransformList transformlist)
      : this(transformlist, false)
    {
    }




    /// <summary>
    /// creates Chained transform from transform List
    /// </summary>
    /// <param name="transformlist">list (chain) of transforms</param>
    /// <param name="createInverted">try create inverted transform</param>
    public ChainedTransform(TransformList transformlist,
                            bool createInverted)
      : base(transformlist[0].Source, transformlist[transformlist.Count - 1].Target)
    {
      
      _TransformList = transformlist;

      //// step over all transforms and set isInversible property
      //isInversible = true;
      //foreach (SRSTransform t in _TransformList)
      //  if (!t.IsInversible)
      //    isInversible = false;

      if (createInverted && !IsInversible)
        throw new ArgumentException(" invalid definition, non invertable transformation cant be inverted");

      IsInverse = createInverted;

      UpdateTransform();
    }






    private TransformList _TransformList;


    public TransformList CoordinateTransformList
    {
      get { return _TransformList; }
      set
      {
        //check, no other Chained transform inside is allowed!!
        foreach (SRSTransform t in value)
          if (t is ChainedTransform)
            throw new ArgumentException("nested "+ this.GetType().ToString() + " is not allowed !");

        _TransformList = value;
        //_inverse = null;
        UpdateTransform();
      }
    }





    public override void Transform(CoordinateList coordList)
    {
      // if this is 2d transform that contains 3d transform and source CoordinateList is 2d
      // process transform using temporary variable (3d point)
      if (coordList.dimension == Dimension.dim2D && contains3dTransform && TransformDimension == Dimension.dim2D)
      {
        Coordinate tmp = new Coordinate(0, 0, 0);
        foreach (ICoordinate coord in coordList)
        {
          tmp.X = coord.X;
          tmp.Y = coord.Y;
          tmp.Z = 0;
          transform(tmp);
          coord.X = tmp.X;
          coord.Y = tmp.Y;
        }
      }
      else
      {
        //transform each point separately is probably faster for CPU...
        foreach (Coordinate c in coordList)
          foreach (SRSTransform t in _TransformList)
          {
            t.Transform(c);
          }
      }
    }





    protected override void transform(ICoordinate point)
    {
      foreach (SRSTransform t in _TransformList)
      {
        t.Transform(point);
      }
    }




    
    //public override void Transform(Coordinate point)
    //{
    //  foreach (SRSTransform t in _TransformList)
    //  {
    //    t.Transform(point);
    //  }
    //}




    //public override CoordStruct Transform(CoordStruct point)
    //{
    //  foreach (SRSTransform t in _TransformList)
    //  {
    //    point = t.Transform(point);
    //  }

    //  return point;
    //}





    /// <summary>
    /// Returns the inverse of this conversion.
    /// </summary>
    /// <returns>IMathTransform that is the reverse of the current conversion.</returns>
    public override SRSTransform Inverse()
    {

      //step over all transforms and set iversible
      foreach (SRSTransform t in _TransformList)
        if (!t.IsInversible)
          throw new InvalidOperationException("transformation is not inversible !");

      TransformList outList = new TransformList();
      //step over all transforms and copy into new List as inverted and oposite order
      foreach (SRSTransform t in _TransformList)
        outList.Insert(0,t.Inverse());

      return new ChainedTransform(outList, !IsInverse);

    }





    ///// <summary>
    ///// Reverses the transformation
    ///// </summary>
    //public override void Invert()
    //{
    //  _TransformList.Reverse();
    //  foreach (ICoordinateTransformation ic in _TransformList)
    //    ic.InversibleTransform.Invert();
    //}




    public override Dimension TransformDimension
    {
      get
      {
        if (_TransformList[0].TransformDimension == Dimension.dim2D &&
            _TransformList[_TransformList.Count - 1].TransformDimension == Dimension.dim2D)
          return Dimension.dim2D;
        else
          return Dimension.dim3D;
      }
    }





    protected bool contains3dTransform
    {
      get
      {

        foreach (SRSTransform t in _TransformList)
          if (t.TransformDimension == Dimension.dim3D)
            return true;

        return false;

      }
    }





    public override bool IsInversible
    {
      get 
      {
        foreach (SRSTransform t in _TransformList)
          if (!t.IsInversible)
            return false;

        return true;
      }
    }





    protected override bool graphCompleted()
    {
      return !(this.CoordinateTransformList == null);
    }





    protected override void updateTransform()
    {
      //nothing to update for Chained transform...
    }
    


  }
}
